/*
 * Package il.ac.biu.cs.grossmm.impl.activeData
 * File SqlSynch.java
 * 
 * This is a part of presence service framework prototype. 
 * See javadoc for more information.
 *  
 * Copyright (C) 2006 Grossmann Mark
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.

 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package il.ac.biu.cs.grossmm.impl.activeData;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class SqlSynch {
	/**
	 * Possible state of the synch
	 */
	static enum State {
		/**
		 * Locked for reading
		 */
		READING, PENDING, WRITING, NOTIFYING, DELETED
	}

	SqlManipulator man;

	static class DeletedException extends Exception {

		/**
		 * Serial UID
		 */
		private static final long serialVersionUID = -5806974137860544946L;
	}

	State state = State.READING;

	int readCount;

	ReentrantLock lock;

	final Condition canRead;

	final Condition canWrite;

	public SqlSynch() {
		lock = new ReentrantLock();
		canRead = lock.newCondition();
		canWrite = lock.newCondition();
	}

	void readLock() throws InterruptedException, DeletedException {
		lock.lock();

		try {
			while (state != State.READING && state != State.NOTIFYING)
				canRead.await();

			if (state == State.DELETED)
				throw new DeletedException();

			readCount++;
		} finally {
			lock.unlock();
		}
	}

	void readUnlock() {
		lock.lock();

		try {
			if (--readCount == 0 && state == State.PENDING)
				canWrite.signal();

		} finally {
			lock.unlock();
		}
	}

	void writeLock(SqlManipulator man) throws InterruptedException,
			DeletedException {
		lock.lock();

		try {
			if (state == State.READING)
				state = State.PENDING;

			while (readCount != 0 || state != State.PENDING) {
				canWrite.await();

				if (state == State.DELETED)
					throw new DeletedException();

				if (state == State.READING)
					state = State.PENDING;
			}

			state = State.WRITING;
			this.man = man;
		} finally {
			lock.unlock();
		}
	}

	SqlManipulator writeToNotifyRelock() throws DeletedException {
		lock.lock();

		try {
			if (state == State.DELETED)
				throw new DeletedException();

			state = State.NOTIFYING;
			canRead.signalAll();
			return man;
		} finally {
			lock.unlock();
		}
	}

	void notifyUnlock() {
		lock.lock();

		try {
			state = State.READING;
			canWrite.signal();
		} finally {
			lock.unlock();
		}
	}

	public void finalLock() {
		lock.lock();

		try {
			state = State.DELETED;
			canWrite.signalAll();
			canRead.signalAll();
		} finally {
			lock.unlock();
		}
	}
}
